package com.yinlijun.websocket.controller;

import com.yinlijun.websocket.protobuf.ProtoMsg;
import com.yinlijun.websocket.protobuf.ProtoMsgHelper;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Brief: 用户socket
 *
 * @author YinLijun <yinlijun2004@gmail.com>
 * @date created in 2020-11-19 11:10
 */
@Data
@Slf4j
@Component
@ServerEndpoint(value = "/channel")
public class UserWebsocket {

    private final Object locker = new Object();
    private Session session;
    private String roomId;
    private String username;

    private static Map<Session, UserWebsocket> USER_WS = new ConcurrentHashMap<Session, UserWebsocket>();

    private void closeSession() {
        try {
            synchronized (locker) {
                session.close();
            }
        } catch (IOException e) {
            log.error("关闭session异常", e);
        }
    }

    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        Map<String, List<String>> param = session.getRequestParameterMap();
        if(!param.containsKey("roomId") || !param.containsKey("username")) {
            log.error("无效的roomId或Username, 断开连接");
            closeSession();
            return;
        }
        username = param.get("username").get(0);
        roomId = param.get("roomId").get(0);
        USER_WS.put(session, this);

        broadcast(ProtoMsgHelper.buildText(username + "加入聊天室~"));
        log.info("【websocket消息】有新的客户端连接, 目前在线人数:{}", USER_WS.size());
    }

    @OnError
    public void onError(Session session, Throwable error) {
        log.error("【websocket消息】session 发生错误 message:{} 关闭连接", error.getMessage());

        UserWebsocket websocket = USER_WS.get(session);
        if(websocket != null) {
            websocket.closeSession();
        }
    }

    @OnClose
    public void onClose(Session session) {
        USER_WS.remove(session);
        broadcast(ProtoMsgHelper.buildText(username + "离开聊天室~"));
        log.info("【websocket消息】客户端连接断开, 目前在线人数总数:{}", USER_WS.size());
    }

    @OnMessage
    public void onMessage(Session session, final ByteBuffer message) {
        final UserWebsocket origin = USER_WS.get(session);
        USER_WS.values().forEach(s -> {
            if(s != origin && s.roomId.equals(origin.roomId)) {
                s.sendMessage(message);
            }
        });
    }

    private void broadcast(ProtoMsg.Msg msg) {
        final byte[] data = msg.toByteArray();
        final ByteBuffer byteBuffer = ByteBuffer.wrap(data);
        final UserWebsocket origin = this;
        USER_WS.values().forEach(s -> {
            if(s != origin && s.roomId.equals(origin.roomId)) {
                s.sendMessage(byteBuffer);
            }
        });
    }

    private void sendMessage(ByteBuffer message){
        sendContentLock(message);
    }

    private void sendContentLock(ByteBuffer content) {
        synchronized (locker) {
            if (!session.isOpen()) {
                return;
            }
            log.warn("给APP客户端发消息:{}", content);
            session.getAsyncRemote().sendBinary(content);
        }
    }
}
